#ifndef T_AUTOMA_MODULES_SLAVES_MOTORIZED_PROTOTYPE_HPP
#define T_AUTOMA_MODULES_SLAVES_MOTORIZED_PROTOTYPE_HPP

#include "../general/generalPrototype.hpp"
#include<map>
#include <tuple>

// simple interface for motorized
// mostly for simple on/off motor, BLDC or servo 
// for one w/ range of motion, use RoboticPrototype
struct MotorizedSlave : public GeneralSlave {
	struct Axis {
		struct NomimalToPhysicalMap {
			struct N2PEntry {
				float nominal = 0;
				float physical = 0;
			};
			std::vector<N2PEntry> entrys;
			bool has(float nomimalIn, float errorAllowance = 0.01);
			float get(float nomimalIn, float errorAllowance = 0.01);
			void add(float nomimalIn, float physicalIn);
		};

		const static int maxAxisNum = 8;
		//int axisMask[maxAxisNum];
		// axis index; min-max
		std::wstring axisFuncName[maxAxisNum];
		// for pump, start with 1
		std::wstring axisCode[maxAxisNum];
		std::wstring axisDirName[maxAxisNum][2];
		// unit used by controller
		std::wstring axisCtrlUnit[maxAxisNum];
		// unit of actual output, like driving a volume (ul) by a linear movement(mm)
		std::wstring axisOutputUnit[maxAxisNum];
		// function belong to axis
		std::wstring axisExFuncCode[maxAxisNum];

		// expanded by command. you can choose to run multiple channel
		int axisSelectableNum = 0;
		int axisActivatedMask = 0;
		// in mm
		float axisRangeMM[maxAxisNum] = { 0 };
		// output unit per control unit
		float axisOutputPerCtrlUnit[maxAxisNum];
		SlaveRef positionSensorOfAxis[maxAxisNum] = { nullptr };
		// towards minimal
		int homeDirection = -1;

		// map a nominal value to physical coordinate, calibrated
		NomimalToPhysicalMap norm2PhyMaps[maxAxisNum];
		//uint32_t fullMask = 0xffffffff;
		bool flagInit = false;
		// different type may use this differently
		bool otherConfigUpdated = false;
		void init();
		void initSimu();
		int getDefinedAxisNum();
	}_motorAxis;

	struct MotorMiscFunctions {
		struct MotorMiscFunction {
			std::wstring name;
			//std::wstring hint;
			std::vector<std::wstring>calls;
			std::wstring unit;
		};
		std::vector<MotorMiscFunction> funcs;
	}_motorMiscFunctions;

	MotorizedSlave();
	MotorizedSlave(GeneralSlave* ref);
	~MotorizedSlave();

	int motorInit(GeneralSlave* ref);
	void motorInit();
	virtual void motorRelease();

	virtual int setOfflineSimu(int flag, float simuAccRateMMPerSecSec);

	virtual int home_able();

	//int isConnected();
	//int virtual readOut() { return 0; }
	virtual std::wstring getCurCoordStr() { return L""; };
	virtual std::wstring getTargetCoordStr() { return L""; };
	virtual std::wstring getLimitedStr() { return L"None"; };
	virtual std::wstring getSpeedStr() { return L""; };
	virtual std::wstring getLoadStr() { return L""; };
	virtual std::wstring getSensorsStr() { return L""; };
	virtual float getCurCoord(int axis) { return 0; };

	// readIndex: see readout
	virtual int associatedSensorReadoutInIdle(uint64_t readIndex);

	// axis index (0~5) 
	//int moveInAxis(int axis, int dir, float distMM);
	//virtual int moveInMode(std::wstring mode, float distOrRate);

	virtual std::wstring getActionStr() { return L""; };

	virtual int stop(int hardStop) { return 0; };
	int miscFunc(std::wstring command, std::wstring& reply);

	void* summarizeFunctionsJson();
	// list not checked
	void* summarizeFunctionsJsonListed(std::map<std::wstring, int> list);

protected:
	const int SLAVE_TIME_OUT_MS = 100;

	virtual void initDefaultMeta();

	void loadAxisDefinition(GeneralSlave* slave);
	int loadAxisFunctionsCore(std::wstring functions);
	void loadAxisParametersCore(std::wstring parameters, int expectedCount);

	virtual void updateAfterConnectAny(HandShakeProtocol* protocol) override;

	//void updateAfterConnectAnyMotor();

protected:
	// if func name matches is listed here, it is used as a axis (in config order)
	virtual std::set<std::wstring> getAxialFuncs();
};

#endif